/*
 * Lona.cpp
 *
 *  Created on: 21/04/2013
 *      Author: gonzalo
 */

#include "Lona.h"
#include "../exceptions/ArgumentInvalidError.h"

#include <iostream>

Lona::Lona(GLuint  program, GLfloat altura_deformacion) :
		Figure(program), cant_puntos(6), altura_deformacion(altura_deformacion), distribution(-1.0, 1.0) {

	/*if (cant_puntos_de_control < 3) {
	 throw ArgumentInvalidError("Cantidad de puntos de control incorrecta %d, min 3",
	 cant_puntos_de_control);
	 }*/

	uint definicion_horizontal = 200;
	uint definicion_vertical = 100;

	GLfloat x, y;
	GLfloat valor;

	vertex_buffer_size = definicion_horizontal * definicion_vertical;
	vertex_buffer = new GLfloat[vertex_buffer_size * 3];

	index_buffer_size = definicion_horizontal * 2 * (definicion_vertical - 1);
	index_buffer = new GLuint[index_buffer_size];

	for (uint j = 0; j < definicion_vertical; j++) {
		for (uint i = 0; i < definicion_horizontal; i++) {
			valor = i;
			x = valor / definicion_horizontal - 0.5;
			valor = j;
			y = valor / definicion_vertical - 0.5;
			set_coords3(vertex_buffer, i + j * definicion_horizontal, x, y, 0.0);
			if (j < definicion_vertical - 1) {
				if (j % 2 == 0) { // derecha
					index_buffer[2 * (i + j * definicion_horizontal)] = i + j * definicion_horizontal;
					index_buffer[2 * (i + j * definicion_horizontal) + 1] = i
							+ definicion_horizontal * (j + 1);
				} else { // izquierda
					index_buffer[2 * (i + j * definicion_horizontal)] = (j + 1) * definicion_horizontal - i
							- 1;
					index_buffer[2 * (i + j * definicion_horizontal) + 1] = (j + 2) * definicion_horizontal
							- i - 1;
				}
			}
		}
	}

	control_points_curva_x_1 = new GLfloat[2 * cant_puntos * 4];
	/*control_points_curva_x_2 = new GLfloat[2 * cant_puntos];
	 control_points_curva_y_1 = new GLfloat[2 * cant_puntos];
	 control_points_curva_y_2 = new GLfloat[2 * cant_puntos];*/
	control_points_curva_x_2 = control_points_curva_x_1 + 2 * cant_puntos;
	control_points_curva_y_1 = control_points_curva_x_2 + 2 * cant_puntos;
	control_points_curva_y_2 = control_points_curva_y_1 + 2 * cant_puntos;

	time = 0;

	for (uint j = 0; j < cant_puntos; j++) {
		valor = j;
		control_points_curva_x_1[2 * j] = valor / (cant_puntos - 1) - 0.5;
		control_points_curva_x_2[2 * j] = valor / (cant_puntos - 1) - 0.5;
		control_points_curva_y_1[2 * j] = valor / (cant_puntos - 1) - 0.5;
		control_points_curva_y_2[2 * j] = valor / (cant_puntos - 1) - 0.5;
		control_points_curva_x_2[2 * j + 1] = 0;
		control_points_curva_y_2[2 * j + 1] = 0;
	}

	// Debug



	// Debug

	this->actualizar_curvas();

	GLuint time_loc = glGetUniformLocation(program, "time");
	if (time_loc >= 0) {
		glUniform1f(time_loc, time);
	}

	// Tiene que esta activado el programa para obtener el id. (glUseProgram)
	GLuint control_points_loc = glGetUniformLocation(program, "puntos_de_control");
	if (control_points_loc >= 0) {
		glUniform2fv(control_points_loc, 4 * cant_puntos, control_points_curva_x_1);
	}
	/*control_points_loc = glGetUniformLocation(program, "puntos_x_2");
	 if (control_points_loc >= 0) {
	 glUniform2fv(control_points_loc, cant_puntos, control_points_curva_x_2);
	 }
	 control_points_loc = glGetUniformLocation(program, "puntos_y_1");
	 if (control_points_loc >= 0) {
	 glUniform2fv(control_points_loc, cant_puntos, control_points_curva_y_1);
	 }
	 control_points_loc = glGetUniformLocation(program, "puntos_y_2");
	 if (control_points_loc >= 0) {
	 glUniform2fv(control_points_loc, cant_puntos, control_points_curva_y_2);
	 }*/
}

Lona::~Lona() {
}

void Lona::cout_control_points(GLfloat * control_points) {
	std::cout << " " << control_points[1];
	for (uint i = 1; i < cant_puntos; i++) {
		std::cout << " " << control_points[2 * i + 1];
	}
	std::cout << std::endl;
}

void Lona::draw(GLuint program, glm::mat4 & world_matrix) {
	glm::mat4 matriz = world_matrix * this->model_matrix;

// Normal Matrix
	glm::mat3 normal_matrix = glm::mat3(1.0f);
	if (this->view_matrix != NULL) {
		glm::mat4 aux = (*this->view_matrix) * world_matrix;
		for (int i = 0; i < 3; i++)
			for (int j = 0; j < 3; j++)
				normal_matrix[i][j] = aux[i][j];
	}

// Bind Normal Matrix
	GLuint location_normal_matrix = glGetUniformLocation(program, "NormalMatrix");
	if (location_normal_matrix >= 0) {
		glUniformMatrix3fv(location_normal_matrix, 1, GL_FALSE, &normal_matrix[0][0]);
	}

// Bind Model Matrix
	GLuint location_model_matrix = glGetUniformLocation(program, "ModelMatrix");
	if (location_model_matrix >= 0) {
		glUniformMatrix4fv(location_model_matrix, 1, GL_FALSE, &matriz[0][0]);
	}

	glEnableClientState(GL_VERTEX_ARRAY);

	glVertexPointer(3, GL_FLOAT, 0, vertex_buffer);

	glDrawElements(GL_TRIANGLE_STRIP, index_buffer_size, GL_UNSIGNED_INT, index_buffer);

	glDisableClientState(GL_VERTEX_ARRAY);

	if (time >= 1.0) {
		this->actualizar_curvas();
		//std::cout << "Actualizado" << std::endl;
		time = 0;
	}

}

void Lona::actualizar_curvas() {
	GLfloat * aux;
	uint j;
	GLfloat valor;
	/*aux = control_points_curva_x_1;
	 control_points_curva_x_1 = control_points_curva_x_2;
	 control_points_curva_x_2 = aux;
	 aux = control_points_curva_y_1;
	 control_points_curva_y_1 = control_points_curva_y_2;
	 control_points_curva_y_2 = aux;*/

	for (uint i = 0; i < cant_puntos; i++) {
		valor = i;
		control_points_curva_x_1[2 * i + 1] = control_points_curva_x_2[2 * i + 1];
		control_points_curva_x_2[2 * i + 1] = distribution(generator);
		control_points_curva_y_1[2 * i + 1] = control_points_curva_y_2[2 * i + 1];
		control_points_curva_y_2[2 * i + 1] = distribution(generator);
	}

	control_points_curva_x_1[1] = 0.0;
	control_points_curva_x_2[1] = 0.0;

	control_points_curva_x_1[2 * cant_puntos - 1] = 0.0;
	control_points_curva_x_2[2 * cant_puntos - 1] = 0.0;

	/*GLuint control_points_loc = glGetUniformLocation(program, "puntos_de_control");
	if (control_points_loc >= 0) {
		glUniform2fv(control_points_loc, 4 * cant_puntos, control_points_curva_x_1);
	}*/
}

void Lona::set_time(GLfloat time) {
	this->time = time;
}

void Lona::actualizar_time(GLfloat speed) {
	time += speed;

	if (time > 1.0) {
		time = 1.0;
	} else if (time == 1.0) {
		//std::cout << "Actualizado" << std::endl;
		time = 0;
	}

	//std::cout << "Time " << time << std::endl;

	//glUseProgram(program);

	/*GLuint time_loc = glGetUniformLocation(program, "time");
	if (time_loc >= 0) {
		glUniform1f(time_loc, this->time);
	}*/
}
